package com.mayflygeek.mayflyask.framework.webmvc.interceptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;

import com.google.common.collect.Lists;

/**
 * @author 虫虫
 * @team 蜉蝣极客
 * @link www.mayflygeek.com
 * @Emial chongchong@mayflygeek.com
 * 
 */
public final class MethodHelper {
	private static final Logger logger = LoggerFactory.getLogger(MethodHelper.class);

	private MethodHelper() {

	}

	public static Object[] getParameterObjects(final Class<?>[] parameterTypes,
			final Object[] providedParameterObjects) {
		Object[] result = new Object[parameterTypes.length];
		for (int i = 0; i < result.length; i++) {
			result[i] = findObjectByType(parameterTypes[i], providedParameterObjects);
		}
		return result;
	}

	private static Object findObjectByType(Class<?> clazz, Object[] providedParameterObjects) {
		for (Object o : providedParameterObjects) {
			if (clazz.isInstance(o)) {
				return o;
			}
		}
		return null;
	}

	public static <A extends Annotation> List<MethodAndAnno<A>> findMethodAndAnnos(Class<?> controllerType,
			Class<A> annoType, List<Class<?>> supportedInjectParameterTypes) {
		final List<MethodAndAnno<A>> result = Lists.newArrayList();
		for (Method method : controllerType.getMethods()) {
			A anno = AnnotationUtils.findAnnotation(method, annoType);
			if (anno != null) {
				if (MethodHelper.isMatchAny(method, supportedInjectParameterTypes)) {
					result.add(new MethodAndAnno<A>(method, anno));
				} else {
					final String info = String.format(
							"忽略执行%s方法, %s签名方法目前只支持注入HttpServletRequest和HttpServletResponse类型参数", method.getName(),
							annoType.getName());
					logger.warn(info);
				}
			}
		}

		// 按order 排序
		Collections.sort(result, new Comparator<MethodAndAnno<?>>() {
			public int compare(MethodAndAnno<?> methodAndAnno1, MethodAndAnno<?> methodAndAnno2) {
				int order1 = (Integer) AnnotationUtils.getValue(methodAndAnno1.getAnno(), "order");
				int order2 = (Integer) AnnotationUtils.getValue(methodAndAnno2.getAnno(), "order");
				return order1 - order2;
			}
		});

		return result;
	}

	static class MethodAndAnno<A extends Annotation> {
		private final A anno;
		private final Method method;

		public MethodAndAnno(Method method, A anno) {
			this.anno = anno;
			this.method = method;
		}

		public A getAnno() {
			return anno;
		}

		public Method getMethod() {
			return method;
		}

	}

	private static boolean isMatchAny(Method method, List<Class<?>> predicatedParameterTypes) {
		Class<?>[] pts = method.getParameterTypes();
		if (pts.length == 0) {
			return true;
		}

		for (Class<?> pt : pts) {
			if (!matchAnyType(pt, predicatedParameterTypes)) {
				return false;
			}
		}
		return true;
	}

	private static boolean matchAnyType(Class<?> source, List<Class<?>> types) {
		for (Class<?> type : types) {
			if (type.isAssignableFrom(source)) {
				return true;
			}
		}
		return false;
	}
}
